﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using Sinacor.Infra.Common.Security.Cryptography;
using System.Data.Common;
using System.IO;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml;


namespace Sinacor.Infra.Tools.ErrorLogCollector
{
    public class ServerCollector
    {
        public LogPackage Collect(DateTime from, DateTime to)
        {
            LogPackage package = new LogPackage();
            package.Server = new Package<ServerEnvironmentData>();

            EventLogCollector eventLogCollector = new EventLogCollector();
            EnvironmentCollector environmentCollector = new EnvironmentCollector();

            package.Server.Environment = environmentCollector.CollectServerEnvironment();

            // Process Manager
            package.Server.Logs.AddRange(eventLogCollector.Collect("BM&FBOVESPA Process Manager", LogSource.ProcessManager, from, to).Cast<LogData>());
            package.Server.Logs.AddRange(eventLogCollector.Collect("ResourceAccessFactory", LogSource.ProcessManager, from, to).Cast<LogData>());
            package.Server.Logs.AddRange(eventLogCollector.Collect("Process Manager - Configuration Manager", LogSource.ProcessManager, from, to).Cast<LogData>());
            package.Server.Logs.AddRange(eventLogCollector.Collect("InteligenceModule Manager - Configuration Manager", LogSource.ProcessManager, from, to).Cast<LogData>());

            // Hoster
            package.Server.Logs.AddRange(eventLogCollector.Collect("BM&FBOVESPA WCF Services Hoster", LogSource.Hoster, from, to).Cast<LogData>());
            package.Server.Logs.AddRange(eventLogCollector.Collect("BM&FBOVESPA WCF Services Hoster Manager", LogSource.Hoster, from, to).Cast<LogData>());

            // Logging
            package.Server.Logs.AddRange(eventLogCollector.Collect("Sinacor Service Logging", LogSource.Logging, from, to).Cast<LogData>());
            package.Server.Logs.AddRange(eventLogCollector.Collect("Logging - Configuration Reader", LogSource.Logging, from, to).Cast<LogData>());

            // STS
            package.Server.Logs.AddRange(eventLogCollector.Collect("STS Hoster", LogSource.SecurityTokenService, from, to).Cast<LogData>());

            // Serviços
            package.Server.Logs.AddRange(eventLogCollector.Collect("Sinacor Service Layer", LogSource.WcfServices, from, to).Cast<LogData>());
            package.Server.Logs.AddRange(eventLogCollector.Collect("Sinacor ExceptionHandling", LogSource.WcfServices, from, to).Cast<LogData>());
            package.Server.Logs.AddRange(eventLogCollector.Collect("SecurityTokenAutenticationProviderFactory", LogSource.WcfServices, from, to).Cast<LogData>());
            package.Server.Logs.AddRange(eventLogCollector.Collect("RenewSession", LogSource.WcfServices, from, to).Cast<LogData>());
            package.Server.Logs.AddRange(eventLogCollector.Collect("CreateSession", LogSource.WcfServices, from, to).Cast<LogData>());
            package.Server.Logs.AddRange(eventLogCollector.Collect("SessionControl.Update", LogSource.WcfServices, from, to).Cast<LogData>());
            package.Server.Logs.AddRange(eventLogCollector.Collect("Sinacor Logging", LogSource.WcfServices, from, to).Cast<LogData>());

            // Banco de dados
            package.Server.Logs.AddRange(this.CollectDataBase(from, to).Cast<LogData>());

            // Arquivos
            package.Server.Logs.AddRange(this.CollectFiles(from, to).Cast<LogData>());


            // Ordena por data
            package.Server.Logs = package.Server.Logs.OrderBy(l => l.Date).Reverse().ToList();

            return package;
        }


        private List<DbLogData> CollectDataBase(DateTime from, DateTime to)
        {
            List<DbLogData> logs = new List<DbLogData>();

            RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Sinacor\Infra.Data");

            if (key != null)
            {
                RegistryKey instanceKey = key.OpenSubKey(@"Instances");

                if (instanceKey != null)
                {
                    string user = (string)key.GetValue("user");
                    string password = (string)key.GetValue("password");
                    int timeout = 90;
                    try
                    {
                        timeout = int.Parse(key.GetValue("timeout").ToString());
                    }
                    catch
                    { timeout = 90; }

                    string datasource = (string)instanceKey.GetValue("Default");
                    string maxPoolSize = (string)key.GetValue("MaxPoolSize");
                    string minPoolSize = (string)key.GetValue("MinPoolSize");
                    string pooling = (string)key.GetValue("Pooling");

                    string connection = "Pooling=" + pooling +
                                        ";Min Pool Size=" + minPoolSize +
                                        ";Max Pool Size=" + maxPoolSize +
                                        ";Password=" + UnmaskConnectionString(password) +
                                        ";User ID=" + user +
                                        ";Data Source=" + datasource +
                                        ";Persist Security Info=True";

                    DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.OracleClient");

                    using (DbConnection conn = factory.CreateConnection())
                    {
                        conn.ConnectionString = connection;
                        conn.Open();

                        using (DbCommand comm = conn.CreateCommand())
                        {
                            comm.CommandText = @"SELECT 
                                                    L.COD_LOG, 
                                                    L.COD_TIPO_LOG, 
                                                    L.DATA_LOG, 
                                                    L.NOME_CLAS, 
                                                    L.NUM_IP, 
                                                    L.COD_IDT_SESS_USUA, 

                                                    NVL(D.COD_DET_LOG, DC.COD_DET_LOG) AS COD_DET_LOG, 
                                                    D.NOME_DET_LOG AS NOME_DET_LOG, 
                                                    NVL(D.TXT_CNTD_DET_LOG, DC.TXT_CNTD_DET_LOG) AS TXT_CNTD_DET_LOG
                                                 FROM 
                                                    TFWLOG L
                                                 LEFT JOIN TFWDET_LOG D ON 
                                                    L.COD_LOG = D.COD_LOG 
                                                 LEFT JOIN TFWDET_LOG_CPLX DC ON 
                                                    L.COD_LOG = DC.COD_LOG 
                                                 WHERE 
                                                    L.DATA_LOG >= :dt_from AND 
                                                    L.DATA_LOG <= :dt_to
                                                 ORDER BY 
                                                    L.COD_LOG, 
                                                    NVL(D.COD_DET_LOG, DC.COD_DET_LOG)";

                            DbParameter param = comm.CreateParameter();
                            param.ParameterName = "dt_from";
                            param.DbType = System.Data.DbType.DateTime;
                            param.Value = from;
                            comm.Parameters.Add(param);

                            param = comm.CreateParameter();
                            param.ParameterName = "dt_to";
                            param.DbType = System.Data.DbType.DateTime;
                            param.Value = to;
                            comm.Parameters.Add(param);

                            using (DbDataReader reader = comm.ExecuteReader())
                            {
                                int codLog = -1;
                                DbLogData data = null;

                                while (reader.Read())
                                {
                                    if (codLog != Convert.ToInt32(reader["COD_LOG"]))
                                    {
                                        codLog = Convert.ToInt32(reader["COD_LOG"]);

                                        data = new DbLogData();

                                        data.LogSource = LogSource.WcfServices;

                                        if (!reader.IsDBNull(reader.GetOrdinal("DATA_LOG")))
                                            data.Date = Convert.ToDateTime(reader["DATA_LOG"]);

                                        data.DbLogCodigo = Convert.ToInt32(reader["COD_LOG"]);
                                        data.DbLogCodigoTipo = Convert.ToInt32(reader["COD_TIPO_LOG"]);

                                        data.DbLogNomeClasse = Convert.ToString(reader["NOME_CLAS"]);
                                        data.DbLogNumeroIp = Convert.ToString(reader["NUM_IP"]);
                                        data.DbLogCodigoIdtSessaoUsuario = Convert.ToString(reader["COD_IDT_SESS_USUA"]);

                                        logs.Add(data);
                                    }

                                    DbLogDataDetail detalhe = new DbLogDataDetail();

                                    detalhe.DbLogCodigo = Convert.ToInt32(reader["COD_LOG"]);

                                    if (!reader.IsDBNull(reader.GetOrdinal("COD_DET_LOG")))
                                        detalhe.DbLogCodigoDetalhe = Convert.ToInt32(reader["COD_DET_LOG"]);
                                    
                                    detalhe.DbLogNomeDetalhe = Convert.ToString(reader["NOME_DET_LOG"]);
                                    detalhe.DbLogConteudo = Convert.ToString(reader["TXT_CNTD_DET_LOG"]);

                                    data.Detalhes.Add(detalhe);
                                }

                            }
                        }
                    }


                }
            }

            return logs;
        }

        private static string UnmaskConnectionString(string maskedConnection)
        {
            if (maskedConnection == null)
                throw new ArgumentNullException("maskedConnection");

            SymmetricCryptographer.Algorithm algorithm = new SymmetricCryptographer.Algorithm();
            byte[] key = { 15, 54, 6, 17, 38, 25, 80, 41 };
            byte[] maskedConnectionString = null;
            byte[] unmaskedConnectionString = null;
            string connectionString = "";

            ProtectedKey protectedKey = ProtectedKey.Create(key);
            SymmetricCryptographer cryptographer = new SymmetricCryptographer(algorithm, protectedKey);

            //string para bytes
            maskedConnectionString = Convert.FromBase64String(maskedConnection);

            //Descriptografa
            unmaskedConnectionString = cryptographer.Decrypt(maskedConnectionString);

            //bytes para string
            connectionString = Encoding.Unicode.GetString(unmaskedConnectionString);

            return connectionString;
        }


        private List<FileLogData> CollectFiles(DateTime from, DateTime to)
        {
            List<FileLogData> logs = new List<FileLogData>();
            string alternativeLogFilePath = "C:\\";

            RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Sinacor\Logging");

            if (key != null)
            {
                string registryKey = (string)key.GetValue("AlternativeLogFilePath");

                if (!String.IsNullOrEmpty(registryKey))
                    alternativeLogFilePath = registryKey;
            }

            string[] files = Directory.GetFiles(alternativeLogFilePath, "ErrorLogging*.log");

            foreach (string file in files)
            {
                DateTime date;

                if (DateTime.TryParseExact(Path.GetFileNameWithoutExtension(file).Replace("ErrorLogging", string.Empty).Trim(), 
                                           "yyyyMMdd HHmm",
                                           System.Globalization.DateTimeFormatInfo.InvariantInfo,
                                           System.Globalization.DateTimeStyles.None,
                                           out date))
                {
                    if (date >= from &&
                        date <= to)
                    {
                        FileLogData data = new FileLogData();

                        data.LogSource = LogSource.WcfServices;
                        data.Date = date;
                        data.FileLogName = Path.GetFileName(file);
                        data.FileLogPath = Path.GetDirectoryName(file);

                        XmlReaderSettings s = new XmlReaderSettings();
                        s.ConformanceLevel = ConformanceLevel.Fragment;

                        XElement elements = new XElement("LogEntryRoot");
                        
                        XPathDocument xPathDocument = new XPathDocument(XmlReader.Create(file, s));
                        foreach (XPathNavigator nav in xPathDocument.CreateNavigator().Select("/*"))
                        {
                            elements.Add(XElement.Parse(nav.OuterXml));
                        }

                        data.Content = elements;

                        logs.Add(data);
                    }
                }
            }

            return logs;
        }
    }
}
